2 Part II Advanced displays

2.1 Preparation

You will find the material for this lecture at https://github.com/cbhurley/CRT2021vis

First install these packages

install.packages(c("GGally", "ISLR2", "naniar"))

and load with

library(GGally)
library(ISLR2)
library(timetk) # for the data
library(palmerpenguins)
library(tidyverse)
library(naniar)

bike <- bike_sharing_daily
bike$season <- c("Winter","Spring", "Summer","Fall")[bike$season]
bike$season <- factor(bike$season, levels = c("Winter","Spring", "Summer","Fall"))  

bike$mnth <- factor(bike$mnth)

bike$holiday <- factor(c("No","Yes")[bike$holiday+1])
bike$workingday <- factor(c("No","Yes")[bike$workingday+1])

bike$yr <- factor(c("2011","2012")[bike$yr+1])

bike$weathersit <- c("clear", "cloudy", "lightP", "heavyP")[bike$weathersit]
bike$weathersit <- factor(bike$weathersit, levels= c("clear", "cloudy", "lightP", "heavyP"))
bike <- droplevels(bike)

2.2 Big data

ISLR2 has an hourly version of the bike data with 8645 observations.

The plot of registered versuscasual users is

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + geom_point()

There is likely lot of overplotting.

Jittering will randomly undo rounding, and using alpha will help too

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + 
  geom_jitter(alpha=.5, size=.5)

Other options are to form 2d bins, count the number of observations in each, and color in proportion to the frequency.

p <- ggplot(data=Bikeshare, aes(x=casual, y=registered))
p+ geom_bin2d()

I prefer to make my colour scale light to dark:

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4")

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

The second plot takes a log of the counts so you get a better spread of colours

p +  
  stat_binhex() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

stat_binhex uses hexagonal binning

A more advanced plot fits a 2d density estimate and draw this as a contour plot.

p + stat_density2d()

# fills in the contours
p + stat_density2d(aes(fill = after_stat(level)), geom = "polygon") 

2.3 Scatterplot matrix

Back to the smaller dataset.

Go to articles tab for info https://ggobi.github.io/ggally/

library(GGally)
ggpairs(bike, columns=c("casual","registered","cnt" ))

You can change what is plotted on lower, upper and diagonal,using lower= etc. Putting in mapping= gives densities for both years separately

ggpairs(bike, mapping = aes(color = yr),
            columns=c("casual","registered","cnt" ),
  lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
  diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

ggpairs can handle factors as well. In this plot yrs is in as a plot variable, and as colour.

ggpairs(bike, mapping = aes(color = yr),
        columns=c("casual","registered","cnt","yr"),
        lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
        diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

2.4 Parallel coordinates

Construction:

s <- sample(nrow(bike),20)
ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr",
           showPoints=TRUE,
           alphaLines=0)

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr",
           showPoints=TRUE,
           alphaLines=1)

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr")

By default, all variables are scaled to mean zero and standard deviation 1, individually. Variables that are positively correlated will have parallel line segments, here registered and cnt.

Other scaling options are:

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr", scale="uniminmax")

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr", scale="globalminmax")

In globalminmax, no scaling is done. Here you can see that most users per day are registered.

To see what certain patterns look like in a parallel coordinate plot, let us look at some fake data

x <- rnorm(100)
y <- x+ .3*rnorm(100)
z <- -y+ .1*rnorm(100)
w <- -z+ .5*rnorm(100)
ggparcoord(data.frame(x,y,z,w), scale="uniminmax")

Positive correlation exhibits as parallel segments.

Negative correlation has crossing. The stronger the negative correlation the smaller the pinch point.

ggparcoord(bike, columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "workingday",
           alphaLines=.4)

In the above we see that there is a difference in the casual-registered association for workingday or not. Both groups have positive association but slopes are different. There are more casual users at the weekend.

Verifying with a scatterplot

ggplot(data=bike, aes(x=casual, y=registered, color=workingday)) + geom_point()

The same pattern was evident in the full bike data.

2.5 Practice 3

Using the penguins dataset, make this plot

Which pair of variables and species has the highest correlation?

Can you find two variables with negative correlation, but positive correlation within each species? This is called Simpson’s paradox.

Using ggparcoord, plot all the numeric variables. Use species as color.

Can you identify presence of positive and negative correlation?

2.6 Missing data

Here we use package naniar. This is the dataset for your assignment. All the NAs are marked in grey.

m <- read_csv("marathon.csv",na=c(""," ","NA"))
vis_miss(m)

An upset plot shows the combination of missing values. Most of the NAs involve the club variable.

gg_miss_upset(m[,-c(22,20)])

Check out the visualisations in naniar for more options, eg the vignette at https://cran.r-project.org/web/packages/naniar/vignettes/naniar-visualisation.html

Ci0tLQp0aXRsZTogIkRhdGEgdmlzdWFsaXNhdGlvbiBpbiBSIgphdXRob3I6ICJDYXRoZXJpbmUgSHVybGV5IgpkYXRlOiAiNy85LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBwYW5kb2NfYXJnczogWyItLW51bWJlci1vZmZzZXQ9MSJdCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQogIAotLS0KCjwhLS0gPHN0eWxlIHR5cGU9InRleHQvY3NzIj4gLS0+CjwhLS0gICBib2R5LCB0ZCB7IC0tPgo8IS0tICAgICBmb250LXNpemU6IDE0cHQ7IC0tPgo8IS0tICAgfSAtLT4KPCEtLSBjb2RlLnJ7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0OyAtLT4KPCEtLSB9IC0tPgo8IS0tIHByZSB7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0IC0tPgo8IS0tIH0gLS0+CjwhLS0gPC9zdHlsZT4gLS0+CgoKCgoKCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RikKYGBgCiMgUGFydCBJSSBBZHZhbmNlZCBkaXNwbGF5cwoKIyMgUHJlcGFyYXRpb24KCllvdSB3aWxsIGZpbmQgdGhlIG1hdGVyaWFsIGZvciB0aGlzIGxlY3R1cmUgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgoKRmlyc3QgaW5zdGFsbCB0aGVzZSBwYWNrYWdlcwoKYGBge3IsIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcyhjKCJHR2FsbHkiLCAiSVNMUjIiLCAibmFuaWFyIikpCmBgYAoKYW5kIGxvYWQgd2l0aAoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KHRpbWV0aykgIyBmb3IgdGhlIGRhdGEKbGlicmFyeShwYWxtZXJwZW5ndWlucykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmFuaWFyKQoKYmlrZSA8LSBiaWtlX3NoYXJpbmdfZGFpbHkKYmlrZSRzZWFzb24gPC0gYygiV2ludGVyIiwiU3ByaW5nIiwgIlN1bW1lciIsIkZhbGwiKVtiaWtlJHNlYXNvbl0KYmlrZSRzZWFzb24gPC0gZmFjdG9yKGJpa2Ukc2Vhc29uLCBsZXZlbHMgPSBjKCJXaW50ZXIiLCJTcHJpbmciLCAiU3VtbWVyIiwiRmFsbCIpKSAgCgpiaWtlJG1udGggPC0gZmFjdG9yKGJpa2UkbW50aCkKCmJpa2UkaG9saWRheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJGhvbGlkYXkrMV0pCmJpa2Ukd29ya2luZ2RheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJHdvcmtpbmdkYXkrMV0pCgpiaWtlJHlyIDwtIGZhY3RvcihjKCIyMDExIiwiMjAxMiIpW2Jpa2UkeXIrMV0pCgpiaWtlJHdlYXRoZXJzaXQgPC0gYygiY2xlYXIiLCAiY2xvdWR5IiwgImxpZ2h0UCIsICJoZWF2eVAiKVtiaWtlJHdlYXRoZXJzaXRdCmJpa2Ukd2VhdGhlcnNpdCA8LSBmYWN0b3IoYmlrZSR3ZWF0aGVyc2l0LCBsZXZlbHM9IGMoImNsZWFyIiwgImNsb3VkeSIsICJsaWdodFAiLCAiaGVhdnlQIikpCmJpa2UgPC0gZHJvcGxldmVscyhiaWtlKQpgYGAKCgojIyBCaWcgZGF0YQoKSVNMUjIgaGFzIGFuIGhvdXJseSB2ZXJzaW9uIG9mIHRoZSBiaWtlIGRhdGEgd2l0aCA4NjQ1ICBvYnNlcnZhdGlvbnMuCgpUaGUgcGxvdCBvZiByZWdpc3RlcmVkIHZlcnN1c2Nhc3VhbCB1c2VycyBpcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlcmUgaXMgbGlrZWx5IGxvdCBvZiBvdmVycGxvdHRpbmcuCgpKaXR0ZXJpbmcgd2lsbCByYW5kb21seSB1bmRvIHJvdW5kaW5nLCBhbmQgdXNpbmcgYWxwaGEgd2lsbCBoZWxwIHRvbwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjUsIHNpemU9LjUpCmBgYAoKT3RoZXIgb3B0aW9ucyBhcmUgdG8gZm9ybSAyZCBiaW5zLCBjb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoLCBhbmQgY29sb3IgaW4KcHJvcG9ydGlvbiB0byB0aGUgZnJlcXVlbmN5LgoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgPC0gZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpCnArIGdlb21fYmluMmQoKQpgYGAKCkkgcHJlZmVyIHRvIG1ha2UgbXkgY29sb3VyIHNjYWxlIGxpZ2h0IHRvIGRhcms6CmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIpCgpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYAoKVGhlIHNlY29uZCBwbG90IHRha2VzIGEgbG9nIG9mIHRoZSBjb3VudHMgc28geW91IGdldCBhIGJldHRlciBzcHJlYWQgb2YgY29sb3VycwoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgKyAgCiAgc3RhdF9iaW5oZXgoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYApgc3RhdF9iaW5oZXhgIHVzZXMgaGV4YWdvbmFsIGJpbm5pbmcKCiBBIG1vcmUgYWR2YW5jZWQgcGxvdCBmaXRzIGEgMmQgZGVuc2l0eSBlc3RpbWF0ZSBhbmQgZHJhdyB0aGlzIGFzIGEgY29udG91ciBwbG90LgogCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifSAKcCArIHN0YXRfZGVuc2l0eTJkKCkKYGBgICAKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9Rn0gCiMgZmlsbHMgaW4gdGhlIGNvbnRvdXJzCnAgKyBzdGF0X2RlbnNpdHkyZChhZXMoZmlsbCA9IGFmdGVyX3N0YXQobGV2ZWwpKSwgZ2VvbSA9ICJwb2x5Z29uIikgCmBgYCAgICAKCiMjIFNjYXR0ZXJwbG90IG1hdHJpeAoKQmFjayB0byB0aGUgc21hbGxlciBkYXRhc2V0LgoKR28gdG8gYXJ0aWNsZXMgdGFiIGZvciBpbmZvCjxodHRwczovL2dnb2JpLmdpdGh1Yi5pby9nZ2FsbHkvPgoKCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShHR2FsbHkpCmdncGFpcnMoYmlrZSwgY29sdW1ucz1jKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiApKQpgYGAKCllvdSBjYW4gY2hhbmdlIHdoYXQgaXMgcGxvdHRlZCBvbiBsb3dlciwgdXBwZXIgYW5kIGRpYWdvbmFsLHVzaW5nCmBsb3dlcj1gIGV0Yy4KUHV0dGluZyBpbiBgbWFwcGluZz1gIGdpdmVzIGRlbnNpdGllcyBmb3IgYm90aCB5ZWFycyBzZXBhcmF0ZWx5CgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKICAKZ2dwYWlycyhiaWtlLCBtYXBwaW5nID0gYWVzKGNvbG9yID0geXIpLAogICAgICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiICksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQoKYGBgCgoKYGdncGFpcnNgIGNhbiBoYW5kbGUgZmFjdG9ycyBhcyB3ZWxsLgpJbiB0aGlzIHBsb3QgYHlyc2AgaXMgaW4gYXMgYSBwbG90IHZhcmlhYmxlLCBhbmQgYXMgY29sb3VyLgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKCmdncGFpcnMoYmlrZSwgbWFwcGluZyA9IGFlcyhjb2xvciA9IHlyKSwKICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiLCJ5ciIpLAogICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKIApgYGAKCiMjIFBhcmFsbGVsIGNvb3JkaW5hdGVzCgpDb25zdHJ1Y3Rpb246CgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKcyA8LSBzYW1wbGUobnJvdyhiaWtlKSwyMCkKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0wKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0xKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIikKYGBgCgpCeSBkZWZhdWx0LCBhbGwgdmFyaWFibGVzIGFyZSBzY2FsZWQgdG8gbWVhbiB6ZXJvIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gMSwgaW5kaXZpZHVhbGx5LgpWYXJpYWJsZXMgdGhhdCBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpbGwgaGF2ZSBwYXJhbGxlbCBsaW5lIHNlZ21lbnRzLCBoZXJlIHJlZ2lzdGVyZWQgYW5kIGNudC4KCk90aGVyIHNjYWxpbmcgb3B0aW9ucyBhcmU6CgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwgc2NhbGU9InVuaW1pbm1heCIpCmdncGFyY29vcmQoYmlrZVtzLF0sIGNvbHVtbnM9bWF0Y2goYygiY2FzdWFsIiwicmVnaXN0ZXJlZCIsImNudCIpLG5hbWVzKGJpa2UpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJ5ciIsIHNjYWxlPSJnbG9iYWxtaW5tYXgiKQoKYGBgCkluIGdsb2JhbG1pbm1heCwgbm8gc2NhbGluZyBpcyBkb25lLiBIZXJlIHlvdSBjYW4gc2VlIHRoYXQgbW9zdCB1c2VycyBwZXIgZGF5IGFyZSByZWdpc3RlcmVkLgoKVG8gc2VlIHdoYXQgY2VydGFpbiBwYXR0ZXJucyBsb29rIGxpa2UgaW4gYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QsIGxldAp1cyBsb29rIGF0IHNvbWUgZmFrZSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKeCA8LSBybm9ybSgxMDApCnkgPC0geCsgLjMqcm5vcm0oMTAwKQp6IDwtIC15KyAuMSpybm9ybSgxMDApCncgPC0gLXorIC41KnJub3JtKDEwMCkKZ2dwYXJjb29yZChkYXRhLmZyYW1lKHgseSx6LHcpLCBzY2FsZT0idW5pbWlubWF4IikKYGBgCgpQb3NpdGl2ZSBjb3JyZWxhdGlvbiBleGhpYml0cyBhcyBwYXJhbGxlbCBzZWdtZW50cy4KCk5lZ2F0aXZlIGNvcnJlbGF0aW9uIGhhcyBjcm9zc2luZy4gVGhlIHN0cm9uZ2VyIHRoZSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiB0aGUgc21hbGxlciB0aGUgcGluY2ggcG9pbnQuCgoKCgpgYGB7cixmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGFyY29vcmQoYmlrZSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gIndvcmtpbmdkYXkiLAogICAgICAgICAgIGFscGhhTGluZXM9LjQpCmBgYAoKSW4gdGhlIGFib3ZlIHdlIHNlZSB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBpbiB0aGUgY2FzdWFsLXJlZ2lzdGVyZWQgYXNzb2NpYXRpb24gZm9yIHdvcmtpbmdkYXkgb3Igbm90LgpCb3RoIGdyb3VwcyBoYXZlIHBvc2l0aXZlIGFzc29jaWF0aW9uIGJ1dCBzbG9wZXMgYXJlIGRpZmZlcmVudC4KVGhlcmUgYXJlIG1vcmUgY2FzdWFsIHVzZXJzIGF0IHRoZSB3ZWVrZW5kLgoKVmVyaWZ5aW5nIHdpdGggYSBzY2F0dGVycGxvdAoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChkYXRhPWJpa2UsIGFlcyh4PWNhc3VhbCwgeT1yZWdpc3RlcmVkLCBjb2xvcj13b3JraW5nZGF5KSkgKyBnZW9tX3BvaW50KCkKYGBgCgpUaGUgc2FtZSBwYXR0ZXJuIHdhcyBldmlkZW50IGluIHRoZSBmdWxsIGJpa2UgZGF0YS4KCgojIyBQcmFjdGljZSAzCgpVc2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0LCBtYWtlIHRoaXMgcGxvdAoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQpnZ3BhaXJzKG5hLm9taXQocGVuZ3VpbnMpLCBtYXBwaW5nID0gYWVzKGNvbG9yID0gc3BlY2llcyksCiAgICAgICAgICAgIGNvbHVtbnM9YygzOjcpLAogIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKYGBgCldoaWNoIHBhaXIgb2YgdmFyaWFibGVzIGFuZCBzcGVjaWVzIGhhcyB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbj8KCkNhbiB5b3UgZmluZCB0d28gdmFyaWFibGVzIHdpdGggbmVnYXRpdmUgY29ycmVsYXRpb24sIGJ1dCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoaW4gZWFjaCBzcGVjaWVzPwpUaGlzIGlzIGNhbGxlZCBTaW1wc29uJ3MgcGFyYWRveC4KClVzaW5nIGBnZ3BhcmNvb3JkYCwgcGxvdCBhbGwgdGhlIG51bWVyaWMgdmFyaWFibGVzLiBVc2Ugc3BlY2llcyBhcyBjb2xvci4KCkNhbiB5b3UgaWRlbnRpZnkgcHJlc2VuY2Ugb2YgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGNvcnJlbGF0aW9uPwoKCmBgYHtyLGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GLCBldmFsPUZ9CgoKZ2dwYXJjb29yZChwZW5ndWlucywgY29sdW1ucz13aGljaChzYXBwbHkocGVuZ3VpbnMsIGlzLm51bWVyaWMpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJzcGVjaWVzIiwKICAgICAgICAgICBhbHBoYUxpbmVzPS40KQoKZ2dwYXJjb29yZChwZW5ndWlucywgY29sdW1ucz13aGljaChzYXBwbHkocGVuZ3VpbnMsIGlzLm51bWVyaWMpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJpc2xhbmQiLAogICAgICAgICAgIGFscGhhTGluZXM9LjQpCgpnZ3BhcmNvb3JkKHBlbmd1aW5zLCBjb2x1bW5zPXdoaWNoKHNhcHBseShwZW5ndWlucywgaXMubnVtZXJpYykpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInNleCIsCiAgICAgICAgICAgYWxwaGFMaW5lcz0uNCkKCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpIZXJlIHdlIHVzZSBwYWNrYWdlIG5hbmlhci4KVGhpcyBpcyB0aGUgZGF0YXNldCBmb3IgeW91ciBhc3NpZ25tZW50LiBBbGwgdGhlIE5BcyBhcmUgbWFya2VkIGluIGdyZXkuCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciJ9Cm0gPC0gcmVhZF9jc3YoIm1hcmF0aG9uLmNzdiIsbmE9YygiIiwiICIsIk5BIikpCnZpc19taXNzKG0pCmBgYAoKQW4gdXBzZXQgcGxvdCBzaG93cyB0aGUgY29tYmluYXRpb24gb2YgbWlzc2luZyB2YWx1ZXMuCk1vc3Qgb2YgdGhlIE5BcyBpbnZvbHZlIHRoZSBjbHViIHZhcmlhYmxlLgoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIifQpnZ19taXNzX3Vwc2V0KG1bLC1jKDIyLDIwKV0pCmBgYAoKQ2hlY2sgb3V0IHRoZSB2aXN1YWxpc2F0aW9ucyBpbiBgbmFuaWFyYCBmb3IgbW9yZSBvcHRpb25zLCBlZyB0aGUgdmlnbmV0dGUgYXQgPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uYW5pYXIvdmlnbmV0dGVzL25hbmlhci12aXN1YWxpc2F0aW9uLmh0bWw+CgojIyBSZWNvbW1lbmRlZCBib29rCgoiVGhlIHBsb3QiLCBKZWFuIEhhbmZmIEtvcmVsaXR6CgpgYGB7ciwgZWNobz1GLCBldmFsPVQsIG91dC53aWR0aD0iMjUlIiwgZmlnLmFsaWduPSdjZW50ZXInfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygicGxvdC5wbmciKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFLCBwdXJsPUZBTFNFfQprbml0cjo6cHVybCgibGVjdDIuUm1kIikKCmBgYAoKCg==